package lucky.xiong.多线程;


import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class MyData{   // MyData.Java ==> MyData.class ==> JVM字节码
    volatile int number = 0;  // 1.2
//  int number = 0;  // 1.1

    public void to60(){
        this.number = 60;
    }

    /** 请注意此时number 前面是加了volatile关键字修`饰的，不保证原子性。 IDEA提示器也有警告标注。
        Non-atomic operation on volatile field 'number'
        会出现 写覆盖。
    * */
    public void addPlusPlus() {
        this.number ++;
    }

    AtomicInteger atomicInteger = new AtomicInteger();

    public void addMyAtomic(){
        atomicInteger.getAndIncrement();
    }
}
/**
 * 1. 验证volatile的可见性
 *  1.1 加入 int number = 0; number 变量之前没有添加volatile关键字修饰,没有可见性
 *  1.2 添加了 volatile ， 可以解决可见性问题
 * 2. 验证volatile 不保证原子性
 *  2.1 原子性：
 *      不可分割，完整性，也即某个线程正在做某个具体业务时，中间不可以被加塞或者被分割，需要整体完善。
 *      要么同时成功，要么同时失败
 *  2.2 volatile 不保证原子性
 *  2.3 why ：会出现写覆盖
 *  2.4 如何解决原子性？
 *      - 加 sync
 *      - juc 的 原子操作 例如 AtomicInteger
 *
 */
public class VolatileDemo {

    //main 是一切方法的运行入口
    public static void main(String[] args) {
        MyData myData = new MyData();


        // 如果保证原子性 ， 应该是 2W
        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                // 每个线程执行1000
                for (int j = 0; j < 1000; j++) {
                    myData.addPlusPlus();
                    myData.addMyAtomic();
                }
            },String.valueOf(i)).start();
        }

        // 需要等待上面20个线程都全部计算完成后，再用main线程取得最终结果值。
        while (Thread.activeCount()>2){
            // 如果线程数还大于2 相当于还没进行完，继续等待
            // main 线程 yield ，从运行状态变为可执行状态（就绪状态）。
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName()+"\t int type ,finally number value: "+ myData.number);
        System.out.println(Thread.currentThread().getName()+"\t AtomicInteger type ,finally number value: "+ myData.atomicInteger.get());



    }

    /**
     * volatile可以保证可见性，及时通知其他线程，主无力内存的值已经被修改。
     */
    private static void seeOkByVolatile() {
        // 资源类
        MyData myData = new MyData();

        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"\t come in");
            try {
                TimeUnit.SECONDS.sleep(3);
            }catch (InterruptedException e){e.printStackTrace();}
            myData.to60();
            System.out.println(Thread.currentThread().getName()+"\t update number value : "+ myData.number);
        },"A1").start();

        // 第二个现场就是我们的main线程
        while (myData.number == 0){
            // main 线程就一直在这里等待循环，直到number值不再等于零 ， 如果没有volatile会一直在这里等待，不可见
        }

        System.out.println(Thread.currentThread().getName()+"\t mission is over, main get value : "+myData.number);
    }
}
